package com.cemp.security.context;


import cemp.common.dto.Response;
import cemp.jwt.JwtCommonUser;
import cemp.jwt.JwtTokenUtil;
import cemp.redis.constant.RedisKey;
import cemp.redis.util.RedisUtils;
import cn.hutool.core.lang.Assert;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.Method;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.cemp.security.config.GlobalErrorCodeConstants;
import com.cemp.security.config.SecurityProperties;
import com.cemp.security.pojo.AuthUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * SecurityContextRepositoryImpl
 *
 * @author 张力方
 * @since 2023/5/31
 **/
@Slf4j
@AllArgsConstructor
public class ServerSecurityContextRepositoryImpl implements ServerSecurityContextRepository {

//    @Value("${switch.test.app}")
//    private Boolean isAppTest;

    private final SecurityProperties securityProperties;
//    private final RedisUtils redisUtils;

    @Override
    public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
        return Mono.empty();
    }

    @Override
    public Mono<SecurityContext> load(ServerWebExchange exchange) {
        log.info("========start load ================");

        if(exchange.getRequest().getMethod().equals(HttpMethod.OPTIONS)){
            exchange.getResponse().getHeaders().set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
            exchange.getResponse().getHeaders().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
            exchange.getResponse().getHeaders().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*");
            exchange.getResponse().getHeaders().set("Access-Control-Allow-Private-Network", "true");
        }

        /* 白名单直接返回空Mono 因为不会走后面的认证流程 */
        boolean anyMatch = securityProperties.getPermitAllUrls()
                .stream()
                .anyMatch(it -> exchange.getRequest().getURI().getPath().contains(it));
        if (anyMatch) {
            log.info("anyMatch");
            return Mono.empty();
        }

        String header = exchange.getRequest().getHeaders().getFirst("User-Agent");

        //测试用
//        if(1==1){
        // 移动端用于测试

//        if(header.toLowerCase().contains("iphone") || header.toLowerCase().contains("android")){
//            JwtCommonUser authUser = new JwtCommonUser();
//            authUser.setUserId(1000L);
//            authUser.setOpenid("9999");
//            // 把令牌传给Security
//            SecurityContext emptyContext = new SecurityContextImpl();
//            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser, null, null);
//            emptyContext.setAuthentication(authentication);
//            return Mono.just(emptyContext);
//        }


        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        StringRedisTemplate stringRedisTemplate = SpringUtil.getBean(StringRedisTemplate.class);    //从容器中获取redisTemplate Bean
        response.setStatusCode(HttpStatus.OK);
        // 获取请求头中的token
        String token = request.getHeaders().getFirst("token");
        log.info("token:"+ token);
        try {
            if(StringUtils.isBlank(token)){
                log.info("token is blank");
                DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(Response.success(GlobalErrorCodeConstants.UNAUTHORIZED)));
                response.writeWith(Mono.just(dataBuffer));
                return Mono.empty();
            }
            Claims claims = JwtTokenUtil.parseJWT(token);
            log.info("jwt is parsed");
            Long userId = ((Integer)claims.get("userid")).longValue() ;
            //匹配 redis
            String key = RedisKey.STOCK_AUTH_USER.concat(userId.toString());
            Optional oToken = Optional.ofNullable(stringRedisTemplate.opsForValue().get(key));
            log.info("userid is "+ userId);
            String rToken = oToken.isPresent() ? oToken.get().toString() : "";
            log.info("oToken is:"+ oToken);
            log.info("rToken is:"+ rToken);
            if( !rToken.equals(token)){
                log.info("oToken equals rToken");
                DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(Response.success(GlobalErrorCodeConstants.TIME_EXPIRED)));
                response.writeWith(Mono.just(dataBuffer));
                return Mono.empty();
            }
            log.info("组装授权对象");
            //组装授权对象
            JwtCommonUser authUser = new JwtCommonUser();
            authUser.setUserId(userId);
            authUser.setOpenid(claims.get("openid").toString());

            // 把令牌传给Security
            SecurityContext emptyContext = new SecurityContextImpl();
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser, null, null);
            emptyContext.setAuthentication(authentication);
            return Mono.just(emptyContext);

        } catch (ExpiredJwtException e){
            log.info("ExpiredJwtException:"+ e.getMessage());
            DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(Response.success(GlobalErrorCodeConstants.TIME_EXPIRED)));
            // 返回token已过期
            response.writeWith(Mono.just(dataBuffer));
            return Mono.empty();
        }catch (Exception e) {
            log.info("Exception:"+ e.getMessage());
            DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(Response.success(GlobalErrorCodeConstants.TOKEN_EXCEPTION)));
            response.writeWith(Mono.just(dataBuffer));
            return Mono.empty();
        }


//
//        String redisKey = String.format("token:%s", token);
//        if (StringUtils.isNotBlank(redisKey)) {
//            // 取用户信息(redis)
//            Boolean isExpire = stringRedisTemplate.hasKey(redisKey);
//            // 判断token是否过期
//            if (Boolean.TRUE.equals(isExpire)) {
//                // 用token去redis取令牌
//                String tokenInfo = stringRedisTemplate.opsForValue().get(redisKey);
//                AuthUser authUser = JSON.parseObject(tokenInfo, AuthUser.class);
//                Assert.notNull(authUser, "获取token异常");
//                //SecurityFrameworkUtils.setLoginUser(exchange, authUser);      //加上租户信息 stock项目中不需要
//                // 刷新缓存时间 不再刷新过期时间
////                stringRedisTemplate.expire(redisKey, securityProperties.getTokenTimeOut(), TimeUnit.MINUTES);
//                // 把令牌传给Security 封装Auth对象 传递给chain
//                SecurityContext emptyContext = new SecurityContextImpl();
//                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser, null, null);
//                emptyContext.setAuthentication(authentication);
//                return Mono.just(emptyContext);
//            } else {
//                DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(Response.success(GlobalErrorCodeConstants.UNAUTHORIZED)));
//                // 返回token已过期
//                response.writeWith(Mono.just(dataBuffer));
//                return Mono.empty();
//            }
//        }
//
//        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(new com.cemp.security.config.Response("9999",false,"503","没有权限","没有权限")));
//        response.writeWith(Mono.just(dataBuffer));      //Mono.just 相当于给Consumer 一个消息，让Consumer处理 Mono 就是一组生产者消费者的关系
//        if(1==1){
//            JSONObject obj = new JSONObject();
//            obj.put("userid","anoy");
//            obj.put("token",token);
//            SecurityContext emptyContext = new SecurityContextImpl();
//            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(obj, null, null);
//            emptyContext.setAuthentication(authentication);
//            return Mono.just(emptyContext);
//        }
        //如果访问的地址不在白名单中，就必须要返回一个认证对象SecurityContext,如果是返回空Mono 就会跑到 chain的ExceptionHandle 中的ServerAuthenticationEntryPoint 处理
//        return Mono.empty();            //返回空Mono 提交给处理器，Mono可以理解成一个异步的消息，提交到队列，让队列再给Consumer消费
    }
}
